import queryString from "query-string";
import type { Stringifiable, StringifiableRecord } from "query-string";
import get from "lodash-es/get";
import { isExternal } from "./validator";

const showToast = uni.showToast
interface ISubPackage {
  root: string;
  pages: Array<string>;
}

export type RouteMatchFunc = (
  path: string,
  query?: Record<string, unknown>
) => { path: string; query?: Record<string, unknown> };
export interface RouteInterceptor {
  canNext: INavigationPermitCallBack;
  message: string;
  /**
   * 反馈类型 默认 modal
   */
  feedType?: "toast" | "modal";
  failToPath: string;
  confirmText?: string;
  cancelText?: string;
}

// 原生非Html页面前缀
let APP_SCHEMA_NATIVE = `minapp://page`;
// 原生内嵌Html前缀
let APP_SCHEMA_HTML = `minapp://html`;
// 小程序
let APP_SCHEMA_WXMINIAPP = `minapp://wxmp`;
// webview页面地址
let APP_WEBVIEW_PATH: string = "";
// 直播webview页面地址
let APP_LIVE_WEBVIEW_PATH: string = "";
// 首页地址
let APP_HOME_PATH: string = "";

let INTERCEPTORS: Record<number, RouteInterceptor> = {};

let pageConfigInstance: Record<string, unknown> = {};

// app页面路径
let APP_PAGE_ROUTES: Record<string, Object> = {};
// app tabbar路径，以区分跳转使用swithTab
let APP_TABBAR_PAGES: Array<string> = [];

const logger = (...params: unknown[]) => {
  console.warn(`[navigator]: `, ...params);
};

const navigate = (
  navFn: Function,
  options?: INavigationOption,
  beforeMessage?: string
) => {
  const sleep = (ts: number) =>
    new Promise((resolve) => setTimeout(() => resolve(true), ts));
  // eslint-disable-next-line no-async-promise-executor
  return new Promise(async (resolve, reject) => {
    if (beforeMessage) {
      showToast({
        title: beforeMessage,
        icon: "none",
      });
      await sleep(1500);
    }
    navFn({
      ...options,
      success(res: any) {
        options?.success && options.success(res);
        resolve(res);
      },
      fail(error: any) {
        options?.fail && options.fail(error);
        reject(error);
      },
    });
  });
};
// 跳转小程序
const navigateWxMiniApp = (options: INavigationOption) => {
  if (!options.appId) {
    return showToast({
      title: "请传入AppId",
    });
  }
  uni.navigateToMiniProgram({
    appId: options.appId || "",
    path: options.url,
    extraData: options.query,
    envVersion: options.miniProgramType || "trial",
    fail(e) {
      options.fail?.(e);
      if (e.errMsg.indexOf('fail cancel') > -1) {
        return;
      }
      uni.showModal({
        title: "提示",
        content: "跳转第三方小程序失败， 是否重试",
        confirmText: "重试",
        cancelText: "取消",
        success(result) {
          if (result.confirm) {
            navigateWxMiniApp(options);
          }
        },
      });
    },
    success(res) {
      options.success?.(res);
    },
    complete(res) {
      options.complete?.(res);
    }
  });
};

const parseNativeRoute = (routePath: string, query?: StringifiableRecord) => {
  if (!routePath || !routePath.trim()) {
    logger("路由地址解析失败", routePath, query);
    throw new Error("路由地址解析失败");
  }
  const matchRoute = (navigator.nativeRouteMap as Record<
    string,
    string | RouteMatchFunc
  >)[routePath];
  if (!matchRoute && APP_PAGE_ROUTES[routePath]) {
    return {
      path: routePath,
      query,
    };
  }
  if (typeof matchRoute === "string") {
    return {
      path: routePath,
      query,
    };
  }
  if (typeof matchRoute === "function") {
    return matchRoute(routePath, query);
  }
  // 均为匹配配置，如果匹配小程序原生地址，返回
  if (APP_PAGE_ROUTES[matchRoute]) {
    return {
      path: routePath,
      query,
    };
  }
  logger("路由地址解析失败", routePath, query);
  throw new Error("路由地址解析失败");
};
/**
 * 解析Route地址
 * @param {*} routeUrlPath 后端传入的route地址，可以是AppSchema、网页链接 后面的参数被截掉了，后续步骤会拼接
 * @returns 对应页面地址
 */
const parseRouteUrl = (routeUrlPath: string, query?: StringifiableRecord) => {
  if (!routeUrlPath) {
    logger("路由地址解析失败", routeUrlPath, query);
    throw new Error("路由地址解析失败");
  }
  // 注入参数，方便进行动态配置一些参数
  for (const key in query) {
    if (Object.prototype.hasOwnProperty.call(query, key)) {
      const value = query[key];
      if (value && `${value}`.trim().indexOf('$g.') === 0) {
        const valueKey = `${value}`.trim().slice(3);
        query[key] = get(getApp({ allowDefault: true }).userInfo, valueKey, '');
      }
    }
  }
  // 项目内部真实地址
  if (routeUrlPath.indexOf("/") === 0) {
    return { path: routeUrlPath, query };
  }
  // 原生地址
  if (routeUrlPath.indexOf(APP_SCHEMA_NATIVE) === 0) {
    const nativeRoute = routeUrlPath.slice(APP_SCHEMA_NATIVE.length);
    return parseNativeRoute(nativeRoute, query);
  }
  // 小程序
  if (routeUrlPath.indexOf(APP_SCHEMA_WXMINIAPP) === 0) {
    return {
      path: routeUrlPath.slice(APP_SCHEMA_WXMINIAPP.length),
      query,
    };
  }
  // 内嵌H5地址
  if (routeUrlPath.indexOf(APP_SCHEMA_HTML) === 0) {
    return {
      path: `${APP_WEBVIEW_PATH}${routeUrlPath.slice(APP_SCHEMA_HTML.length)}`,
      query,
    };
  } else if (isExternal(routeUrlPath)) {
    let webviewPath = APP_WEBVIEW_PATH;
    return {
      path: `${webviewPath}`,
      query: {
        ...query,
        url: encodeURIComponent(
          queryString.stringifyUrl({
            query,
            url: routeUrlPath,
          })
        ),
      },
    };
  }
  logger("路由地址解析失败", routeUrlPath, query);
  throw new Error("路由地址解析失败");
};
const getPageInStack = (url: string): [number, any | null] => {
  const pages = getCurrentPages().reverse();
  const index = pages.findIndex((page) => url === `/${page.route}`);
  if (index > 0) {
    return [index, pages[index]];
  }
  return [-1, null];
};
/**
 * 获取当前页面带参数的路径
 */
const getCurrentFullPath = (ignoreQuery?: boolean) => {
  const pages = getCurrentPages();
  if (pages.length <= 0) {
    return null;
  }
  const currentPage = pages[pages.length - 1];
  if (ignoreQuery) {
    return `/${currentPage.route}`
  }
  const queryText = queryString.stringify({ ...currentPage.options, $taroTimestamp: undefined });
  return `/${currentPage.route}${queryText ? '?' : ''}${queryText}`;
};
/**
 * 是否相同的页面路径，包含参数是否一样
 * @param path1 路径1
 * @param path2 路径2 
 * @description TODO 可能需要优化参数对比情况，目前query-string里面做了排序，可以保证经过navigator方式跳转的参数顺序一致性；保证对比结果，单独调用此方法需要自己保证参数顺序
 */
const isSamePath = (path1?: string, path2?: string) => {
  if (path1 === path2) {
    return true;
  }
  // TODO 可能需要优化参数对比情况，目前query-string里面做了排序，可以保证经过navigator方式跳转的参数顺序一致性；保证对比结果，单独调用此方法需要自己保证参数顺序
  return false
}
export const navigator = {
  nativeRouteMap: {},
  setAppSchemaNative(appSchemaNative: string) {
    APP_SCHEMA_NATIVE = appSchemaNative || APP_SCHEMA_NATIVE;
    return this;
  },
  setAppSchemaHtml(appSchemaHtml: string) {
    APP_SCHEMA_HTML = appSchemaHtml || APP_SCHEMA_HTML;
    return this;
  },
  setAppSchemaWXminiApp(appSchemaWXminiApp: string) {
    APP_SCHEMA_WXMINIAPP = appSchemaWXminiApp || APP_SCHEMA_WXMINIAPP;
    return this;
  },
  setAppWebviewPath(appWebviewPath: string) {
    APP_WEBVIEW_PATH = appWebviewPath || APP_WEBVIEW_PATH;
    return this;
  },
  setAppLiveWebviewPath(appLiveWebviewPath: string) {
    APP_LIVE_WEBVIEW_PATH = appLiveWebviewPath || APP_LIVE_WEBVIEW_PATH;
    return this
  },
  setAppHomePath(homePath: string) {
    APP_HOME_PATH = homePath || APP_HOME_PATH;
    return this;
  },
  setPages(pages: Record<string, unknown>) {
    pageConfigInstance = pages;
    return this;
  },
  setInterceptors(interceptors: Record<number, RouteInterceptor>) {
    INTERCEPTORS = interceptors || INTERCEPTORS;
    return this;
  },
  getCurrentFullPath,
  getAppSchemaWXminiApp() {
    return APP_SCHEMA_WXMINIAPP
  },
  init() {
    APP_PAGE_ROUTES = (pageConfigInstance.pages as Array<any>)
      .map((item) => ({ ...item }))
      .concat(
        ((pageConfigInstance.subPackages || []) as Array<ISubPackage>).reduce(
          (
            result: Array<{ path: string }>,
            subPackItem: { root: string; pages: Array<string> }
          ) => {
            return result.concat(
              (subPackItem.pages || []).map(
                (subPage: string | { path: string }) => {
                  return typeof subPage === "string"
                    ? {
                      path: `${subPackItem.root}/${subPage}`,
                    }
                    : {
                      ...subPage,
                      path: `${subPackItem.root}/${subPage.path}`,
                    };
                }
              )
            );
          },
          []
        )
      )
      .reduce((result: Record<string, Object>, pageItem) => {
        result[`/${pageItem.path}`] = { ...pageItem };
        return result;
      }, {});

    APP_TABBAR_PAGES = ((((pageConfigInstance.tabBar || {}) as Record<
      string,
      unknown
    >).list || []) as Array<{ pagePath: string }>).map(
      (tabItem) => `/${tabItem.pagePath}`
    );
    return this;
  },

  /**
   * 跳转路由封装
   * @param {String|Object} options 跳转配置对象或者路由地址
   * @param {String} options.url 跳转路由地址
   * @param {Function} options.query 跳转路由参数
   * @param {String} options.animationType 窗口显示的动画效果
   * @param {Number} options.animationDuration 窗口显示的动画效果
   * @param {Object} options.events 页面间通信接口，用于监听被打开页面发送到当前页面的数据
   * @param {Function} options.success 窗口显示的动画效果
   * @param {Function} options.fail 窗口显示的动画效果
   * @param {Function} options.complete 窗口显示的动画效果
   * @param {Function} options.permitCallBack 权限处理回调，此回调存在不会执行默认权限校验失败拦截处理
   * @param {String} beforeMessage 跳转前的提示，在提示后才会进行跳转
   * @returns
   */
  async navigateTo(
    options: INavigationOption | string,
    beforeMessage?: string
  ) {
    let isReLaunch = false;
    let isRedirect = false;
    if (typeof options === "string") {
      options = {
        url: options,
      };
    } else {
      beforeMessage = beforeMessage || options.beforeMessage;
      isRedirect = options.isRedirect || false;
      isReLaunch = options.isReLaunch || false;
    }
    const { url, query: pathQuery } = queryString.parseUrl(options.url || "", { decode: false });
    const originQuery = {
      ...pathQuery,
      ...options.query,
      $taroTimestamp: undefined
    } as StringifiableRecord;
    const { path: resultPath, query } = parseRouteUrl(url, originQuery);
    const permitType =
      Number(query?.permitCode) ||
      (((APP_PAGE_ROUTES[resultPath] || {}) as StringifiableRecord)
        .permitCode as number) ||
      (0 as number);
    const permitCallBack = options.permitCallBack;
    const userInfo = getApp({ allowDefault: true }).userInfo;

    const noPermissionDefaultHandle = (interceptorItem: RouteInterceptor) => {
      const { message, feedType: messageType = 'modal', failToPath, confirmText = "确定", cancelText = "取消" } = interceptorItem
      const successHandle = () => {
        const successPath = queryString.stringifyUrl(
          {
            url: resultPath,
            query: query as StringifiableRecord,
          },
          {
            strict: false,
            encode: false,
          }
        );
        const routeParams = {
          url: `${failToPath}${failToPath.indexOf("?") > -1 ? "&" : "?"
            }fromPath=${encodeURIComponent(
              getCurrentFullPath() || ""
            )}&successPath=${encodeURIComponent(successPath)}`,
        };
        
        if (isRedirect) {
          uni.redirectTo(routeParams);
        } else if (isReLaunch) {
          uni.reLaunch(routeParams);
        } else {
          uni.navigateTo(routeParams);
        }
      };
      if (messageType === "toast") {
        showToast({
          title: message,
          icon: "none",
          success: () => {
            setTimeout(successHandle, 1500);
          },
        });
      } else {
        uni.showModal({
          title: "提示",
          content: message,
          confirmText,
          cancelText,
          success: (res) => {
            res.confirm && successHandle();
            const isWillToHomePaths = ['/pso/sub_package/common/redirect/index', '/pso/sub_package/auth/register_disease/index', '/pso/sub_package/auth/authrize_qrcode/index'];
            if (res.cancel && isWillToHomePaths.some(path => getCurrentFullPath(true)?.indexOf(path) === 0)) {
              uni.switchTab({
                url: APP_HOME_PATH
              });
            }
          },
        });
      }
    };

    const passAllAssert =
      !permitType ||
      Object.keys(INTERCEPTORS)
        .map(Number)
        .sort((a, b) => a - b)
        .every((permitCode) => {
          const permitCodeNo = Number(permitCode);
          const interceptorItem = INTERCEPTORS[permitCodeNo];
          // console.log('permitType & permitCodeNo' , permitType, permitCodeNo, permitType & permitCodeNo)
          if (
            permitType & permitCodeNo &&
            !interceptorItem.canNext(
              permitType,
              resultPath,
              query,
              userInfo,
              options as INavigationOption
            )
          ) {
            permitCallBack &&
              permitCallBack(
                permitCodeNo,
                resultPath,
                query,
                userInfo,
                options as INavigationOption
              );
            !permitCallBack &&
              noPermissionDefaultHandle(interceptorItem);
            return false;
          }
          return true;
        });
    if (!passAllAssert) {
      logger("路由权限校验未通过", resultPath, query, permitType);
      return null;
    }
    // 小程序
    if (options?.url?.indexOf(APP_SCHEMA_WXMINIAPP) === 0) {
      navigateWxMiniApp({
        url: queryString.stringifyUrl({
          url: resultPath,
          query: query as StringifiableRecord
        }),
        success: options?.success,
        fail: options?.fail,
        complete: options?.complete,
        ...query
      });
      return;
    }
    const isTabBarPage = APP_TABBAR_PAGES.indexOf(url) > -1;
    const navFunc = isTabBarPage
      ? uni.switchTab
      : isReLaunch
        ? uni.reLaunch
        : uni.navigateTo;
    const resultUrl = isTabBarPage ? url : queryString.stringifyUrl(
      {
        url: resultPath,
        query: query as StringifiableRecord,
      },
      {
        strict: false,
        encode: false,
      }
    );
    // 如果当前已经在该页面，且参数一直，不需要跳转, 防止单个页面在短时间内重复跳转
    const samePath = isSamePath(getCurrentFullPath() || "", resultUrl);
    if (samePath) {
      logger(`相同页面路径及参数，无需跳转；当前页面：${getCurrentFullPath()}，目标页面： ${resultUrl}`);
      return;
    }
    return await navigate(
      isRedirect && !isTabBarPage ? uni.redirectTo : navFunc,
      {
        ...options,
        url: resultUrl,
      },
      beforeMessage
    );
  },
  /**
   * 返回
   * @param {*} options
   * @returns
   */
  async navigateBack(options?: INavigationOption, beforeMessage?: string) {
    // 当前页面为根页面时，返回主页
    if (getCurrentPages().length <= 1) {
      console.warn("返回失败，回到首页！");
      return await this.navigateTo({
        ...options,
        beforeMessage: beforeMessage || options?.beforeMessage,
        url: APP_HOME_PATH,
      });
    }
    options = options || {};
    return await navigate(
      uni.navigateBack,
      { ...options, isBack: true },
      beforeMessage || options.beforeMessage
    );
  },
  /**
   * 返回指定页面，页面栈没有该页面执行跳转
   * @param {*} options 返回页面路径或者对象
   * @param {String} options.url 返回页面路径
   */
  async navigateBackTo(
    options: INavigationOption | string,
    beforeMessage?: string
  ) {
    if (typeof options === "string") {
      options = {
        url: options,
      };
    } else {
      beforeMessage = beforeMessage || options.beforeMessage;
    }
    options.url = options.url || "";
    const queryIndex = options.url.indexOf("?");
    const routePath =
      queryIndex > -1 ? queryString.parseUrl(options.url).url : options.url;
    const [index] = getPageInStack(routePath);
    if (index >= 0) {
      options.delta = index;
      return await navigate(
        uni.navigateBack,
        { ...options, isBack: true },
        beforeMessage
      );
    }
    return await this.navigateTo(options, beforeMessage);
  },
  // 跳转H5
  async navigateToH5(path: string, title?: string, beforeMessage: string = "") {
    return await navigator.navigateTo(
      {
        url: `${APP_WEBVIEW_PATH}?url=${encodeURIComponent(
          path
        )}&title=${title}`,
        query: {
          title,
        },
      },
      beforeMessage
    );
  },
  createH5RouteUrl(
    path: string,
    options?: { title?: string;[key: string]: Stringifiable }
  ) {
    const h5Path = queryString.stringifyUrl(
      {
        url: path,
        query: options,
      },
      {
        strict: false,
        encode: false,
      }
    );
    return `${APP_WEBVIEW_PATH}?url=${encodeURIComponent(
      h5Path
    )}&title=${options?.title || ""}`;
  },
  /**
   * 注册原生路由配置
   * @param {Record<string, string | function>} routeConfig
   */
  registerRouteConfig(routeConfig: Record<string, string | RouteMatchFunc>) {
    navigator.nativeRouteMap = {
      ...navigator.nativeRouteMap,
      ...routeConfig,
    };
  },
  /**
   * 路径是否在排除权限的列表
   * @param resultPath 
   * @returns 
   */
  isExcludePermitRoute(resultPath?: string | null) {
    return [
      "/sub-pages/login/index"
    ].some((path) => resultPath?.indexOf(path) === 0);
  },
  /**
   * 是否相同的页面路径，包含参数是否一样
   * @param path1 路径1
   * @param path2 路径2 
   */
  isSamePath: isSamePath
};

export function getAppPageRoute() {
  return APP_PAGE_ROUTES;
}
export default navigator;
